﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using DotNetCommon;
using DotNetCommon.Data;
using DotNetCommon.Extensions;

namespace ConsoleTest
{
    public class Program
    {
        static void Main(string[] args)
        {
            Test1();
            Test2();
            Test3();
            Test4();
            Test5();

            Test6();
            Console.WriteLine("比对完成!");
            Console.Read();
        }

        private static void Test6()
        {
            var zi = new Zi
            {
                Id = 1,
                Name = "小明",
                Addr = "天明路",
            };

            var config = new AutoMapper.MapperConfiguration(cfg => cfg.CreateMap<Zi, Fu>());
            var mapper = new AutoMapper.Mapper(config);
            var fu = mapper.Map<Fu>(zi);
            //false automap不考虑继承关系
            var b = zi == fu;

            fu = zi.Mapper<Fu>();
            //true dotnetmapper考虑继承关系
            b = zi == fu;

            var wrapper = new Wrapper
            {
                Fu = zi,
                Zi = zi
            };

            config = new AutoMapper.MapperConfiguration(cfg =>
            {
                cfg.CreateMap<Wrapper, WrapperDto>();
                cfg.CreateMap<Fu, FuDto>();
                cfg.CreateMap<Zi, ZiDto>();
            });
            mapper = new AutoMapper.Mapper(config);
            var wrapperDto = mapper.Map<WrapperDto>(wrapper);
            //false
            b = wrapperDto.Fu == wrapperDto.Zi;

            wrapperDto = wrapper.Mapper<WrapperDto>();
            b = wrapperDto.Fu == wrapperDto.Zi;



        }

        public class Wrapper
        {
            public Fu Fu { get; set; }
            public Zi Zi { get; set; }
        }

        public class WrapperDto
        {
            public FuDto Fu { get; set; }
            public ZiDto Zi { get; set; }
        }

        public class Fu
        {
            public int Id { get; set; }
            public string Name { get; set; }
        }
        public class Zi : Fu
        {
            public string Addr { get; set; }
        }
        public class FuDto
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public FuDto Fu2 { get; set; }
        }
        public class ZiDto : FuDto
        {
            public string Addr { get; set; }
        }

        #region 简单转换 Person => PersonDto 同类型属性17个 无嵌套类型
        private static Func<TFrom, TDest> getMapperFunc<TFrom, TDest>()
        {
            var st = new Stopwatch();
            st.Start();
            var para = Expression.Parameter(typeof(TFrom), "src");

            //var res=new TDest();
            var variable = Expression.Variable(typeof(TDest), "res");
            var newExp = Expression.New(typeof(TDest).GetConstructor(new Type[0]));
            var assign = Expression.Assign(variable, newExp);

            // 属性赋值
            var assignProps = new List<BinaryExpression>();
            var props = typeof(TFrom).GetProperties();
            var destProps = typeof(TDest).GetProperties();
            foreach (var prop in destProps)
            {
                if (prop.CanWrite)
                {
                    var srcProp = props.FirstOrDefault(i => i.Name == prop.Name);
                    if (srcProp.Name == prop.Name)
                    {
                        //属性
                        var assignProp = Expression.Assign(Expression.Property(variable, prop.Name), Expression.MakeMemberAccess(para, srcProp));
                        assignProps.Add(assignProp);
                    }
                }
            }

            //return res;
            var labelTarget = Expression.Label(typeof(TDest));
            var labelExpression = Expression.Label(labelTarget, variable);
            var gotoExpression = Expression.Return(labelTarget, variable, typeof(TDest));

            var expressions = new List<Expression>();
            expressions.Add(assign);
            expressions.AddRange(assignProps);
            expressions.Add(gotoExpression);
            expressions.Add(labelExpression);
            //组装表达式块,编译生成委托
            var block = Expression.Block(typeof(TDest), new ParameterExpression[] { variable }, expressions);
            var mapperFunc = Expression.Lambda<Func<TFrom, TDest>>(block, para).Compile();
            st.Stop();
            Console.WriteLine($"编译表达式树耗时: {st.ElapsedMilliseconds}毫秒");
            return mapperFunc;
        }
        private static void Test1()
        {
            var person = new Person()
            {
                Id = 1,
                Name = "Test",
                Addr = "天明路",
                Age = 18,
                Description = "描述",
                Email = "123456789@qq.com",
                IdentityNo = "410123200001021234",
                Intro = "个性签名",
                Nick = "昵称",
                Phone = "15012345678",
                Picture = "https://iconfont.alicdn.com/t/ff73b7fb-7199-4356-8d10-5dd133bac7a9.png",
                Birth = DateTime.Parse("2000-01-02"),
                QQ = "123456789",
                Score = 98.5,
                Sex = EnumSex.Male,
                WeiXin = "weixinname",
                Books = new List<string> { "语文", "数学" },
            };

            var tmpList = new List<Person>();
            for (var i = 0; i < 10000; i++)
            {
                tmpList.Add(person.ToJson().ToObject<Person>());
            };
            PersonDto dto = null;

            var list = new List<Performance>();
            var count = 50;

            Console.WriteLine();
            Console.WriteLine($"=============开始性能比对 {count} 次,每次执行Person到PersonDto的转换1万次============");

            #region 原生
            var st4 = new Stopwatch();
            st4.Start();
            for (var i = 0; i < count; i++)
            {
                var st2 = new Stopwatch();
                st2.Start();
                for (var j = 0; j < 10000; j++)
                {
                    var inst = tmpList[j];
                    dto = new PersonDto
                    {
                        Addr = inst.Addr,
                        WeiXin = inst.WeiXin,
                        Sex = inst.Sex,
                        Score = inst.Score,
                        Age = inst.Age,
                        Birth = inst.Birth,
                        Books = inst.Books,
                        Description = inst.Description,
                        Email = inst.Email,
                        Id = inst.Id,
                        IdentityNo = inst.IdentityNo,
                        Intro = inst.Intro,
                        Name = inst.Name,
                        Nick = inst.Nick,
                        Phone = inst.Phone,
                        Picture = inst.Picture,
                        QQ = inst.QQ,
                    };
                }
                st2.Stop();
                list.Add(new Performance
                {
                    Type = 1,
                    Counter = i + 1,
                    Value = st2.ElapsedMilliseconds
                });
            }
            st4.Stop();
            Console.WriteLine($"原生 总耗时: {st4.ElapsedMilliseconds}毫秒 平均: {st4.ElapsedMilliseconds / (count + 0.0)}毫秒");
            list.Add(new Performance
            {
                Type = 11,
                Counter = 0,
                Value = st4.ElapsedMilliseconds
            });
            #endregion

            #region 表达式树
            var func = getMapperFunc<Person, PersonDto>();
            st4 = new Stopwatch();
            st4.Start();
            for (var i = 0; i < count; i++)
            {
                var st2 = new Stopwatch();
                st2.Start();
                for (var j = 0; j < 10000; j++)
                {
                    var inst = tmpList[j];
                    dto = func(inst);
                }
                st2.Stop();
                list.Add(new Performance
                {
                    Type = 4,
                    Counter = i + 1,
                    Value = st2.ElapsedMilliseconds
                });
            }
            st4.Stop();
            Console.WriteLine($"表达式树 总耗时: {st4.ElapsedMilliseconds}毫秒 平均: {st4.ElapsedMilliseconds / (count + 0.0)}毫秒");
            list.Add(new Performance
            {
                Type = 44,
                Counter = 0,
                Value = st4.ElapsedMilliseconds
            });
            #endregion

            #region AutoMapper
            var config = new AutoMapper.MapperConfiguration(cfg => cfg.CreateMap<Person, PersonDto>());
            var mapper = new AutoMapper.Mapper(config);
            dto = mapper.Map<PersonDto>(person);
            Thread.Sleep(2000);

            var st3 = new Stopwatch();
            st3.Start();
            for (var i = 0; i < count; i++)
            {
                var st2 = new Stopwatch();
                st2.Start();
                for (var j = 0; j < 10000; j++)
                {
                    dto = mapper.Map<PersonDto>(tmpList[j]);
                }
                st2.Stop();
                list.Add(new Performance
                {
                    Type = 2,
                    Counter = i + 1,
                    Value = st2.ElapsedMilliseconds
                });
            }
            st3.Stop();
            Console.WriteLine($"AutoMapper 总耗时: {st3.ElapsedMilliseconds}毫秒 平均: {st3.ElapsedMilliseconds / (count + 0.0)}毫秒");
            list.Add(new Performance
            {
                Type = 22,
                Counter = 0,
                Value = st3.ElapsedMilliseconds
            });
            #endregion            

            #region DotNetCommon Mapper
            var personDto2 = person.Mapper<PersonDto>();
            Thread.Sleep(2000);
            Console.WriteLine($"DotNetCommon Mapper预热完毕");

            var st = new Stopwatch();
            st.Start();
            for (var i = 0; i < count; i++)
            {
                var st2 = new Stopwatch();
                st2.Start();
                for (var j = 0; j < 10000; j++)
                {
                    dto = tmpList[j].Mapper<PersonDto>();
                }
                st2.Stop();
                list.Add(new Performance
                {
                    Type = 3,
                    Counter = i + 1,
                    Value = st2.ElapsedMilliseconds
                });
            }
            st.Stop();
            Console.WriteLine($"DotNetCommonMapper 总耗时: {st.ElapsedMilliseconds}毫秒 平均: {st.ElapsedMilliseconds / (count + 0.0)}毫秒");
            list.Add(new Performance
            {
                Type = 33,
                Counter = 0,
                Value = st.ElapsedMilliseconds
            });
            #endregion

            //输出统计数据
            Console.WriteLine();
            Console.WriteLine($"{"".PadRight(10)}{" 原生".PadRight(15)}{" 表达式树".PadRight(15)}{"AutoMapper".PadRight(15)}{"DotNetCommonMapper".PadRight(10)}");
            for (var i = 0; i < count; i++)
            {
                var per1 = list.FirstOrDefault(p => p.Type == 1 && p.Counter == (i + 1));
                var per4 = list.FirstOrDefault(p => p.Type == 4 && p.Counter == (i + 1));
                var per2 = list.FirstOrDefault(p => p.Type == 2 && p.Counter == (i + 1));
                var per3 = list.FirstOrDefault(p => p.Type == 3 && p.Counter == (i + 1));
                Console.WriteLine($"{("第" + (i + 1) + "次:").PadRight(10)}{(per1.Value + "毫秒").PadRight(15)}{(per4.Value + "毫秒").PadRight(15)}{(per2.Value + "毫秒").PadRight(15)}{(per3.Value + "毫秒").PadRight(10)}");
            }
            var total1 = list.FirstOrDefault(i => i.Type == 11);
            var avg1 = total1.Value / (count + 0.0);
            var total4 = list.FirstOrDefault(i => i.Type == 44);
            var avg4 = total4.Value / (count + 0.0);
            var total2 = list.FirstOrDefault(i => i.Type == 22);
            var avg2 = total2.Value / (count + 0.0);
            var total3 = list.FirstOrDefault(i => i.Type == 33);
            var avg3 = total3.Value / (count + 0.0);
            Console.WriteLine($"{"合计:".PadRight(10)}{(avg1 + "/" + total1.Value + "毫秒").PadRight(15)}{(avg4 + "/" + total4.Value + "毫秒").PadRight(15)}{(avg2 + "/" + total2.Value + "毫秒").PadRight(15)}{(avg3 + "/" + total3.Value + "毫秒").PadRight(10)}");
        }
        #region 模型
        public enum EnumSex
        {
            Male, FeMale
        }
        public class Person
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public string Addr { get; set; }
            public string IdentityNo { get; set; }
            public EnumSex Sex { get; set; }
            public DateTime Birth { get; set; }
            public string Description { get; set; }
            public int Age { get; set; }
            public string Nick { get; set; }
            public string QQ { get; set; }
            public string Email { get; set; }
            public string Phone { get; set; }
            public string WeiXin { get; set; }
            public string Picture { get; set; }
            public string Intro { get; set; }
            public double Score { get; set; }
            public List<string> Books { get; set; }
        }

        public class PersonDto
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public string Addr { get; set; }
            public string IdentityNo { get; set; }
            public EnumSex Sex { get; set; }
            public DateTime Birth { get; set; }
            public string Description { get; set; }
            public int Age { get; set; }
            public string Nick { get; set; }
            public string QQ { get; set; }
            public string Email { get; set; }
            public string Phone { get; set; }
            public string WeiXin { get; set; }
            public string Picture { get; set; }
            public string Intro { get; set; }
            public double Score { get; set; }
            public List<string> Books { get; set; }
        }
        #endregion
        #endregion

        #region 直接对集合的转换
        public static void Test2()
        {
            RoslynMapper.RegisterConvert<Org, OrgDto>();
            Thread.Sleep(3000);

            var tmpList = new List<List<Org>>();
            var tmp = new List<Org>();
            for (var i = 0; i < 10000; i++)
            {
                tmp.Add(new Org
                {
                    Id = 1,
                    Active = true,
                    CreatetTime = DateTime.Now,
                    Name = "1",
                    ParentId = 1,
                });
            };
            var count = 50;
            for (var i = 0; i < count; i++)
            {
                tmpList.Add(tmp.ToJson().ToObject<List<Org>>());
            }
            List<OrgDto> dto = null;

            var list = new List<Performance>();

            Console.WriteLine();
            Console.WriteLine($"=============开始性能比对 {count} 次,每次执行List<Person>到List<PersonDto>的转换1次,集合中有1万条数据============");

            #region 原生
            var st4 = new Stopwatch();
            st4.Start();
            for (var i = 0; i < count; i++)
            {
                var st2 = new Stopwatch();
                st2.Start();
                dto = tmpList[i].Select(p => new OrgDto
                {
                    Id = p.Id,
                    Active = p.Active
                }).ToList();
                st2.Stop();
                list.Add(new Performance
                {
                    Type = 1,
                    Counter = i + 1,
                    Value = st2.ElapsedMilliseconds
                });
            }
            st4.Stop();
            Console.WriteLine($"原生 总耗时: {st4.ElapsedMilliseconds}毫秒 平均: {st4.ElapsedMilliseconds / (count + 0.0)}毫秒");
            list.Add(new Performance
            {
                Type = 11,
                Counter = 0,
                Value = st4.ElapsedMilliseconds
            });
            #endregion

            #region AutoMapper
            var config = new AutoMapper.MapperConfiguration(cfg => cfg.CreateMap<Org, OrgDto>());
            var mapper = new AutoMapper.Mapper(config);
            dto = mapper.Map<List<OrgDto>>(tmp);
            Thread.Sleep(2000);

            var st3 = new Stopwatch();
            st3.Start();
            for (var i = 0; i < count; i++)
            {
                var st2 = new Stopwatch();
                st2.Start();
                dto = mapper.Map<List<OrgDto>>(tmpList[i]);
                st2.Stop();
                list.Add(new Performance
                {
                    Type = 2,
                    Counter = i + 1,
                    Value = st2.ElapsedMilliseconds
                });
            }
            st3.Stop();
            Console.WriteLine($"AutoMapper 总耗时: {st3.ElapsedMilliseconds}毫秒 平均: {st3.ElapsedMilliseconds / (count + 0.0)}毫秒");
            list.Add(new Performance
            {
                Type = 22,
                Counter = 0,
                Value = st3.ElapsedMilliseconds
            });
            #endregion            

            #region DotNetCommon Mapper
            var personDto2 = tmp.Mapper<List<OrgDto>>();
            Thread.Sleep(2000);
            Console.WriteLine($"DotNetCommon Mapper预热完毕");

            var st = new Stopwatch();
            st.Start();
            for (var i = 0; i < count; i++)
            {
                var st2 = new Stopwatch();
                st2.Start();
                dto = tmpList[i].Mapper<List<OrgDto>>();
                st2.Stop();
                list.Add(new Performance
                {
                    Type = 3,
                    Counter = i + 1,
                    Value = st2.ElapsedMilliseconds
                });
            }
            st.Stop();
            Console.WriteLine($"DotNetCommonMapper 总耗时: {st.ElapsedMilliseconds}毫秒 平均: {st.ElapsedMilliseconds / (count + 0.0)}毫秒");
            list.Add(new Performance
            {
                Type = 33,
                Counter = 0,
                Value = st.ElapsedMilliseconds
            });
            #endregion

            //输出统计数据
            Console.WriteLine();
            Console.WriteLine($"{"".PadRight(10)}{" 原生".PadRight(15)}{"AutoMapper".PadRight(15)}{"DotNetCommonMapper".PadRight(10)}");
            for (var i = 0; i < count; i++)
            {
                var per1 = list.FirstOrDefault(p => p.Type == 1 && p.Counter == (i + 1));
                var per2 = list.FirstOrDefault(p => p.Type == 2 && p.Counter == (i + 1));
                var per3 = list.FirstOrDefault(p => p.Type == 3 && p.Counter == (i + 1));
                Console.WriteLine($"{("第" + (i + 1) + "次:").PadRight(10)}{(per1.Value + "毫秒").PadRight(15)}{(per2.Value + "毫秒").PadRight(15)}{(per3.Value + "毫秒").PadRight(10)}");
            }
            var total1 = list.FirstOrDefault(i => i.Type == 11);
            var avg1 = total1.Value / (count + 0.0);
            var total2 = list.FirstOrDefault(i => i.Type == 22);
            var avg2 = total2.Value / (count + 0.0);
            var total3 = list.FirstOrDefault(i => i.Type == 33);
            var avg3 = total3.Value / (count + 0.0);
            Console.WriteLine($"{"合计:".PadRight(10)}{(avg1 + "/" + total1.Value + "毫秒").PadRight(15)}{(avg2 + "/" + total2.Value + "毫秒").PadRight(15)}{(avg3 + "/" + total3.Value + "毫秒").PadRight(10)}");
        }
        #region 模型
        public class Org
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public string Pwd { get; set; }
            public DateTime CreatetTime { get; set; }
            public bool? Active { get; set; }
            public int? ParentId { get; set; }
        }

        public class OrgDto
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public bool? Active { get; set; }
        }
        #endregion
        #endregion

        #region 循环引用
        public static void Test3()
        {
            var tmpList = new List<PersonExt>();
            for (var i = 0; i < 10000; i++)
            {
                var inst = new PersonExt
                {
                    Id = 1,
                    Name = "Test",
                    Addr = "天明路",
                    Age = 18,
                    Description = "描述",
                    Email = "123456789@qq.com",
                    IdentityNo = "410123200001021234",
                    Intro = "个性签名",
                    Nick = "昵称",
                    Phone = "15012345678",
                    Picture = "https://iconfont.alicdn.com/t/ff73b7fb-7199-4356-8d10-5dd133bac7a9.png",
                    Birth = DateTime.Parse("2000-01-02"),
                    QQ = "123456789",
                    Score = 98.5,
                    Sex = EnumSex.Male,
                    WeiXin = "weixinname",
                    Books = new List<string> { "语文", "数学" },
                };
                inst.Self = inst;
                tmpList.Add(inst);
            };

            var list = new List<Performance>();
            PersonExtDto dto = null;
            var count = 50;

            Console.WriteLine();
            Console.WriteLine($"=============开始性能比对 {count} 次,每次执行PersonExt到PersonExtDto的转换1万次(自循环)============");

            #region 原生
            var st4 = new Stopwatch();
            st4.Start();
            for (var i = 0; i < count; i++)
            {
                var st2 = new Stopwatch();
                st2.Start();
                for (var j = 0; j < 10000; j++)
                {
                    var inst = tmpList[j];
                    dto = new PersonExtDto
                    {
                        Addr = inst.Addr,
                        WeiXin = inst.WeiXin,
                        Sex = inst.Sex,
                        Score = inst.Score,
                        Age = inst.Age,
                        Birth = inst.Birth,
                        Books = inst.Books,
                        Description = inst.Description,
                        Email = inst.Email,
                        Id = inst.Id,
                        IdentityNo = inst.IdentityNo,
                        Intro = inst.Intro,
                        Name = inst.Name,
                        Nick = inst.Nick,
                        Phone = inst.Phone,
                        Picture = inst.Picture,
                        QQ = inst.QQ,
                    };
                    dto.Self = dto;
                    Debug.Assert(dto.Self == dto);
                }
                st2.Stop();
                list.Add(new Performance
                {
                    Type = 1,
                    Counter = i + 1,
                    Value = st2.ElapsedMilliseconds
                });
            }
            st4.Stop();
            Console.WriteLine($"原生 总耗时: {st4.ElapsedMilliseconds}毫秒 平均: {st4.ElapsedMilliseconds / (count + 0.0)}毫秒");
            list.Add(new Performance
            {
                Type = 11,
                Counter = 0,
                Value = st4.ElapsedMilliseconds
            });
            #endregion

            #region AutoMapper
            var config = new AutoMapper.MapperConfiguration(cfg => cfg.CreateMap<PersonExt, PersonExtDto>());
            var mapper = new AutoMapper.Mapper(config);
            dto = mapper.Map<PersonExtDto>(tmpList.FirstOrDefault());
            Thread.Sleep(2000);
            var st3 = new Stopwatch();
            st3.Start();
            for (var i = 0; i < count; i++)
            {
                var st2 = new Stopwatch();
                st2.Start();
                for (var j = 0; j < 10000; j++)
                {
                    dto = mapper.Map<PersonExtDto>(tmpList[j]);
                    Debug.Assert(dto.Self == dto);
                }
                st2.Stop();
                list.Add(new Performance
                {
                    Type = 2,
                    Counter = i + 1,
                    Value = st2.ElapsedMilliseconds
                });
            }
            st3.Stop();
            Console.WriteLine($"AutoMapper 总耗时: {st3.ElapsedMilliseconds}毫秒 平均: {st3.ElapsedMilliseconds / (count + 0.0)}毫秒");
            list.Add(new Performance
            {
                Type = 22,
                Counter = 0,
                Value = st3.ElapsedMilliseconds
            });
            #endregion

            #region DotNetCommon Mapper
            dto = tmpList.FirstOrDefault().Mapper<PersonExtDto>();
            Thread.Sleep(2000);
            Console.WriteLine($"DotNetCommon Mapper预热完毕");
            var st = new Stopwatch();
            st.Start();
            for (var i = 0; i < count; i++)
            {
                var st2 = new Stopwatch();
                st2.Start();
                for (var j = 0; j < 10000; j++)
                {
                    dto = tmpList[j].Mapper<PersonExtDto>();
                    Debug.Assert(dto.Self == dto);
                }
                st2.Stop();
                list.Add(new Performance
                {
                    Type = 3,
                    Counter = i + 1,
                    Value = st2.ElapsedMilliseconds
                });
            }
            st.Stop();
            Console.WriteLine($"DotNetCommonMapper 总耗时: {st.ElapsedMilliseconds}毫秒 平均: {st.ElapsedMilliseconds / (count + 0.0)}毫秒");
            list.Add(new Performance
            {
                Type = 33,
                Counter = 0,
                Value = st.ElapsedMilliseconds
            });
            #endregion

            //输出统计数据
            Console.WriteLine();
            Console.WriteLine($"{"".PadRight(10)}{" 原生".PadRight(15)}{"AutoMapper".PadRight(15)}{"DotNetCommonMapper".PadRight(10)}");
            for (var i = 0; i < count; i++)
            {
                var per1 = list.FirstOrDefault(p => p.Type == 1 && p.Counter == (i + 1));
                var per2 = list.FirstOrDefault(p => p.Type == 2 && p.Counter == (i + 1));
                var per3 = list.FirstOrDefault(p => p.Type == 3 && p.Counter == (i + 1));
                Console.WriteLine($"{("第" + (i + 1) + "次:").PadRight(10)}{(per1?.Value + "毫秒").PadRight(15)}{(per2?.Value + "毫秒").PadRight(15)}{(per3?.Value + "毫秒").PadRight(10)}");
            }
            var total1 = list.FirstOrDefault(i => i.Type == 11);
            var avg1 = total1?.Value / (count + 0.0);
            var total2 = list.FirstOrDefault(i => i.Type == 22);
            var avg2 = total2.Value / (count + 0.0);
            var total3 = list.FirstOrDefault(i => i.Type == 33);
            var avg3 = total3.Value / (count + 0.0);
            Console.WriteLine($"{"合计:".PadRight(10)}{(avg1 + "/" + total1?.Value + "毫秒").PadRight(15)}{(avg2 + "/" + total2.Value + "毫秒").PadRight(15)}{(avg3 + "/" + total3.Value + "毫秒").PadRight(10)}");
        }

        public class PersonExt : Person
        {
            public PersonExt Self { get; set; }
        }
        public class PersonExtDto : PersonDto
        {
            public PersonExtDto Self { get; set; }
        }

        #endregion

        #region 嵌套类型转换
        public static void Test4()
        {
            var tmpList = new List<Outer>();
            for (var i = 0; i < 10000; i++)
            {
                var inst = new Outer
                {
                    Id = 1,
                    Name = "Test",
                    Addr = "天明路",
                    Age = 18,
                    Description = "描述",
                    Email = "123456789@qq.com",
                    IdentityNo = "410123200001021234",
                    Intro = "个性签名",
                    Nick = "昵称",
                    Phone = "15012345678",
                    Picture = "https://iconfont.alicdn.com/t/ff73b7fb-7199-4356-8d10-5dd133bac7a9.png",
                    Birth = DateTime.Parse("2000-01-02"),
                    QQ = "123456789",
                    Score = 98.5,
                    Sex = EnumSex.Male,
                    WeiXin = "weixinname",
                    Books = new List<string> { "语文", "数学" },
                    Inner = new Inner
                    {
                        Id = 1,
                        Addr = "提阿尼公路",
                        Age = 18,
                        Name = "Test",
                        Score = 98.5
                    }
                };
                tmpList.Add(inst);
            };

            var list = new List<Performance>();
            OuterDto dto = null;
            var count = 50;

            Console.WriteLine();
            Console.WriteLine($"=============开始性能比对 {count} 次,每次执行Outer到OuterDto的转换1万次(自循环)============");

            #region 原生
            var st4 = new Stopwatch();
            st4.Start();
            for (var i = 0; i < count; i++)
            {
                var st2 = new Stopwatch();
                st2.Start();
                for (var j = 0; j < 10000; j++)
                {
                    var inst = tmpList[j];
                    dto = new OuterDto
                    {
                        Addr = inst.Addr,
                        WeiXin = inst.WeiXin,
                        Sex = inst.Sex,
                        Score = inst.Score,
                        Age = inst.Age,
                        Birth = inst.Birth,
                        Books = inst.Books,
                        Description = inst.Description,
                        Email = inst.Email,
                        Id = inst.Id,
                        IdentityNo = inst.IdentityNo,
                        Intro = inst.Intro,
                        Name = inst.Name,
                        Nick = inst.Nick,
                        Phone = inst.Phone,
                        Picture = inst.Picture,
                        QQ = inst.QQ,
                        Inner = new InnerDto
                        {
                            Id = inst.Inner.Id,
                            Score = inst.Inner.Score,
                            Age = inst.Inner.Age,
                            Addr = inst.Inner.Addr,
                            Name = inst.Inner.Name,
                        }
                    };
                }
                st2.Stop();
                list.Add(new Performance
                {
                    Type = 1,
                    Counter = i + 1,
                    Value = st2.ElapsedMilliseconds
                });
            }
            st4.Stop();
            Console.WriteLine($"原生 总耗时: {st4.ElapsedMilliseconds}毫秒 平均: {st4.ElapsedMilliseconds / (count + 0.0)}毫秒");
            list.Add(new Performance
            {
                Type = 11,
                Counter = 0,
                Value = st4.ElapsedMilliseconds
            });
            #endregion

            #region AutoMapper
            var config = new AutoMapper.MapperConfiguration(cfg =>
            {
                cfg.CreateMap<Outer, OuterDto>();
                cfg.CreateMap<Inner, InnerDto>();
            });
            var mapper = new AutoMapper.Mapper(config);
            dto = mapper.Map<OuterDto>(tmpList.FirstOrDefault());
            Thread.Sleep(2000);
            var st3 = new Stopwatch();
            st3.Start();
            for (var i = 0; i < count; i++)
            {
                var st2 = new Stopwatch();
                st2.Start();
                for (var j = 0; j < 10000; j++)
                {
                    dto = mapper.Map<OuterDto>(tmpList[j]);
                }
                st2.Stop();
                list.Add(new Performance
                {
                    Type = 2,
                    Counter = i + 1,
                    Value = st2.ElapsedMilliseconds
                });
            }
            st3.Stop();
            Console.WriteLine($"AutoMapper 总耗时: {st3.ElapsedMilliseconds}毫秒 平均: {st3.ElapsedMilliseconds / (count + 0.0)}毫秒");
            list.Add(new Performance
            {
                Type = 22,
                Counter = 0,
                Value = st3.ElapsedMilliseconds
            });
            #endregion

            #region DotNetCommon Mapper
            dto = tmpList.FirstOrDefault().Mapper<OuterDto>();
            Thread.Sleep(2000);
            Console.WriteLine($"DotNetCommon Mapper预热完毕");
            var st = new Stopwatch();
            st.Start();
            for (var i = 0; i < count; i++)
            {
                var st2 = new Stopwatch();
                st2.Start();
                for (var j = 0; j < 10000; j++)
                {
                    dto = tmpList[j].Mapper<OuterDto>();
                }
                st2.Stop();
                list.Add(new Performance
                {
                    Type = 3,
                    Counter = i + 1,
                    Value = st2.ElapsedMilliseconds
                });
            }
            st.Stop();
            Console.WriteLine($"DotNetCommonMapper 总耗时: {st.ElapsedMilliseconds}毫秒 平均: {st.ElapsedMilliseconds / (count + 0.0)}毫秒");
            list.Add(new Performance
            {
                Type = 33,
                Counter = 0,
                Value = st.ElapsedMilliseconds
            });
            #endregion

            //输出统计数据
            Console.WriteLine();
            Console.WriteLine($"{"".PadRight(10)}{" 原生".PadRight(15)}{"AutoMapper".PadRight(15)}{"DotNetCommonMapper".PadRight(10)}");
            for (var i = 0; i < count; i++)
            {
                var per1 = list.FirstOrDefault(p => p.Type == 1 && p.Counter == (i + 1));
                var per2 = list.FirstOrDefault(p => p.Type == 2 && p.Counter == (i + 1));
                var per3 = list.FirstOrDefault(p => p.Type == 3 && p.Counter == (i + 1));
                Console.WriteLine($"{("第" + (i + 1) + "次:").PadRight(10)}{(per1?.Value + "毫秒").PadRight(15)}{(per2?.Value + "毫秒").PadRight(15)}{(per3?.Value + "毫秒").PadRight(10)}");
            }
            var total1 = list.FirstOrDefault(i => i.Type == 11);
            var avg1 = total1?.Value / (count + 0.0);
            var total2 = list.FirstOrDefault(i => i.Type == 22);
            var avg2 = total2.Value / (count + 0.0);
            var total3 = list.FirstOrDefault(i => i.Type == 33);
            var avg3 = total3.Value / (count + 0.0);
            Console.WriteLine($"{"合计:".PadRight(10)}{(avg1 + "/" + total1?.Value + "毫秒").PadRight(15)}{(avg2 + "/" + total2.Value + "毫秒").PadRight(15)}{(avg3 + "/" + total3.Value + "毫秒").PadRight(10)}");
        }

        public class Outer : Person
        {
            public Inner Inner { get; set; }
        }
        public class Inner
        {
            public int Id { get; set; }
            public int Age { get; set; }
            public double Score { get; set; }
            public string Name { get; set; }
            public string Addr { get; set; }
        }
        public class OuterDto : PersonDto
        {
            public InnerDto Inner { get; set; }
        }
        public class InnerDto
        {
            public int Id { get; set; }
            public int Age { get; set; }
            public double Score { get; set; }
            public string Name { get; set; }
            public string Addr { get; set; }
        }
        #endregion

        #region 树状结构
        public static void Test5()
        {
            List<OrgNode> tmpList = null;
            #region 构造平铺的数据
            tmpList = new List<OrgNode>()
            {
                new OrgNode()
                {
                    Id=1,
                    Name="河南",
                    Pwd="henan",
                    CreatetTime=DateTime.Now,
                    Active=null
                },
                new OrgNode()
                {
                    Id=2,
                    Name="河北",
                    Pwd="hebei",
                    CreatetTime=DateTime.Now,
                    Active=null
                },
                new OrgNode()
                {
                    Id=3,
                    Name="郑州",
                    ParentId=1,
                    Active=true,
                    CreatetTime=DateTime.Now,
                    Pwd="zhengzhou"
                },
                new OrgNode()
                {
                    Id=4,
                    Name="开封",
                    ParentId=1,
                    Active=true,
                    CreatetTime=DateTime.Now,
                    Pwd="kaifeng"
                },
                new OrgNode()
                {
                    Id=5,
                    Name="中原区",
                    ParentId=3,
                    Active=true,
                    CreatetTime=DateTime.Now,
                    Pwd="zhongyuanqu"
                },
                new OrgNode()
                {
                    Id=6,
                    Name="金水区",
                    ParentId=3,
                    Active=true,
                    CreatetTime=DateTime.Now,
                    Pwd="jinshuiqu"
                },
                new OrgNode()
                {
                    Id=7,
                    Name="文化路",
                    ParentId=6,
                    Active=true,
                    CreatetTime=DateTime.Now,
                    Pwd="wenhualu"
                }
            };
            #endregion
            var orgs = tmpList.FetchToTree(o => o.Id, o => o.ParentId, i => i.Children);

            List<OrgNodeDto> tree = null;

            var list = new List<Performance>();
            var count = 50;

            Console.WriteLine();
            Console.WriteLine($"=============开始性能比对 {count} 次,每次执行 OrgNodes 到 OrgNodeDto 的转换(树状)============");
            #region 原生
            var st4 = new Stopwatch();
            st4.Start();
            for (var i = 0; i < count; i++)
            {
                var st2 = new Stopwatch();
                st2.Start();
                for (int q = 0; q < 10000; q++)
                {
                    tree = tmpList.Select(i => new OrgNodeDto
                    {
                        Id = i.Id,
                        Active = i.Active,
                        Name = i.Name,
                        Children = i.Children.IsNullOrEmpty() ? null : i.Children.Select(p => new OrgNodeDto
                        {
                            Id = p.Id,
                            Active = p.Active,
                            Name = p.Name,
                            Children = p.Children.IsNullOrEmpty() ? null : p.Children.Select(k => new OrgNodeDto
                            {
                                Id = k.Id,
                                Active = k.Active,
                                Name = k.Name,
                                Children = k.Children.IsNullOrEmpty() ? null : new List<OrgNodeDto>()
                            }).ToList()
                        }).ToList()
                    }).ToList();
                }
                st2.Stop();
                list.Add(new Performance
                {
                    Type = 1,
                    Counter = i + 1,
                    Value = st2.ElapsedMilliseconds
                });
            }
            st4.Stop();
            Console.WriteLine($"原生 总耗时: {st4.ElapsedMilliseconds}毫秒 平均: {st4.ElapsedMilliseconds / (count + 0.0)}毫秒");
            list.Add(new Performance
            {
                Type = 11,
                Counter = 0,
                Value = st4.ElapsedMilliseconds
            });
            #endregion

            #region AutoMapper
            var config = new AutoMapper.MapperConfiguration(cfg =>
            {
                cfg.CreateMap<OrgNode, OrgNodeDto>();
            });
            var mapper = new AutoMapper.Mapper(config);
            tree = mapper.Map<List<OrgNodeDto>>(tmpList);
            Thread.Sleep(2000);
            var st3 = new Stopwatch();
            st3.Start();
            for (var i = 0; i < count; i++)
            {
                var st2 = new Stopwatch();
                st2.Start();
                for (int q = 0; q < 10000; q++)
                {
                    tree = mapper.Map<List<OrgNodeDto>>(tmpList);
                }
                st2.Stop();

                list.Add(new Performance
                {
                    Type = 2,
                    Counter = i + 1,
                    Value = st2.ElapsedMilliseconds
                });
            }
            st3.Stop();
            Console.WriteLine($"AutoMapper 总耗时: {st3.ElapsedMilliseconds}毫秒 平均: {st3.ElapsedMilliseconds / (count + 0.0)}毫秒");
            list.Add(new Performance
            {
                Type = 22,
                Counter = 0,
                Value = st3.ElapsedMilliseconds
            });
            #endregion

            #region DotNetCommon Mapper
            tree = tmpList.Mapper<List<OrgNodeDto>>();
            Thread.Sleep(2000);
            Console.WriteLine($"DotNetCommon Mapper预热完毕");
            var st = new Stopwatch();
            st.Start();
            for (var i = 0; i < count; i++)
            {
                var st2 = new Stopwatch();
                st2.Start();
                for (int q = 0; q < 10000; q++)
                {
                    tree = tmpList.Mapper<List<OrgNodeDto>>();
                }
                st2.Stop();
                list.Add(new Performance
                {
                    Type = 3,
                    Counter = i + 1,
                    Value = st2.ElapsedMilliseconds
                });
            }
            st.Stop();
            Console.WriteLine($"DotNetCommonMapper 总耗时: {st.ElapsedMilliseconds}毫秒 平均: {st.ElapsedMilliseconds / (count + 0.0)}毫秒");
            list.Add(new Performance
            {
                Type = 33,
                Counter = 0,
                Value = st.ElapsedMilliseconds
            });
            #endregion

            //输出统计数据
            Console.WriteLine();
            Console.WriteLine($"{"".PadRight(10)}{" 原生".PadRight(15)}{"AutoMapper".PadRight(15)}{"DotNetCommonMapper".PadRight(10)}");
            for (var i = 0; i < count; i++)
            {
                var per1 = list.FirstOrDefault(p => p.Type == 1 && p.Counter == (i + 1));
                var per2 = list.FirstOrDefault(p => p.Type == 2 && p.Counter == (i + 1));
                var per3 = list.FirstOrDefault(p => p.Type == 3 && p.Counter == (i + 1));
                Console.WriteLine($"{("第" + (i + 1) + "次:").PadRight(10)}{(per1?.Value + "毫秒").PadRight(15)}{(per2?.Value + "毫秒").PadRight(15)}{(per3?.Value + "毫秒").PadRight(10)}");
            }
            var total1 = list.FirstOrDefault(i => i.Type == 11);
            var avg1 = total1?.Value / (count + 0.0);
            var total2 = list.FirstOrDefault(i => i.Type == 22);
            var avg2 = total2.Value / (count + 0.0);
            var total3 = list.FirstOrDefault(i => i.Type == 33);
            var avg3 = total3.Value / (count + 0.0);
            Console.WriteLine($"{"合计:".PadRight(10)}{(avg1 + "/" + total1?.Value + "毫秒").PadRight(15)}{(avg2 + "/" + total2.Value + "毫秒").PadRight(15)}{(avg3 + "/" + total3.Value + "毫秒").PadRight(10)}");
        }

        public class OrgNode : Org
        {
            public List<OrgNode> Children { get; set; }
        }

        public class OrgNodeDto : OrgDto
        {
            public List<OrgNodeDto> Children { get; set; }
        }
        #endregion
    }

    public class Performance
    {
        public int Type { get; set; }
        public int Counter { get; set; }
        public double Value { get; set; }
    }
}
